<template>
  <view class="l-barrage">
    <block v-for="(item, index) in items" :key="index">
      <view
        class="barrage-item"
        :id="item.id"
        :data-duration="item.duration"
        :data-trackindex="item.trackIndex"
        :style="{
          top: `${item.top}`,
          'animation-duration': `${Number(item.duration)}s`,
        }"
        @animationend="handleEnd(item.id, item.trackIndex)"
      >
        {{ item.text }}
      </view>
    </block>
    <!-- 计算每条弹幕实际的宽度 -->
    <view class="bullet-container">{{ currentBullet }}</view>
  </view>
</template>

<script>
/* eslint-disable */
let cycle // 定时器
export default {
  name: 'Barrage',
  props: {
    barrageData: {
      type: Array,
      default: () => {
        return [
          { blessContent: '神秘人刚刚已经报名' },
        ]
      },
    },
  },
  data() {
    return {
      i: 0,
      items: [],
      currentBullet: '',
      speed: 50,
      duration: 10,
      trackHeight: 32, // 单位为rpx
      targetRight: 0,
      tracks: [],
      bulletInfo: {},
    }
  },
  mounted() {
    !!this.barrageData.length && this.start(this.barrageData)
    const query = uni.createSelectorQuery().in(this)
    query
      .select('.l-barrage')
      .boundingClientRect(data => {
        this.targetRight = data.right
        this.targetW = data.width
        this.targetH = data.height
        const trackNum = Math.floor(this.targetH / this.rpx2px(this.trackHeight))

        // 初始时设置轨道为空闲
        this.tracks = new Array(trackNum).fill('idle')
        this.bullets = new Array(trackNum).fill([])
      })
      .exec()
  },
  unmounted() {
    cycle && clearInterval(cycle)
  },
  watch: {
    barrageData() {
      !!this.barrageData.length && this.start(this.barrageData)
    },
    // i() {
    //   setInterval(() => {
    //     if (this.i === this.barrageData.length) {
    //       this.start(this.barrageData)
    //     }
    //   }, this.barrageData.length * 1000)
    // },
  },
  methods: {
    rpx2px(rpx) {
      return (rpx / 750) * wx.getSystemInfoSync().windowWidth
    },
    start(items = []) {
      this.items = []
      this.i = 0
      cycle && clearInterval(cycle)
      const len = items.length
      cycle = setInterval(() => {
        if (this.i < len) {
          this.push(items[this.i])
        } else {
          clearInterval(cycle)
        }
      }, 2000)
    },
    // 插入一条弹幕数据
    push(item) {
      this.currentBullet = item.blessContent

      this.$nextTick(() => {
        let duration // 计算在相同速度下每条弹幕动画需要的时间
        const query = uni.createSelectorQuery().in(this)
        query
          .select('.bullet-container')
          .boundingClientRect(async data => {
            if (this.speed) {
              duration = (this.targetW + data.width) / this.speed
            } else {
              duration = this.duration
            }
            // 记录下当前弹幕的宽度和运动时间
            this.bulletInfo = {
              width: data.width,
              duration,
            }
            const trackIndex = await this.getTrackIndex(this.i)

            // 计算弹幕的top, 10px为行间距
            const trackTop = trackIndex * this.rpx2px(this.trackHeight) + 10 * (trackIndex + 1) + 'px'

            const id = 's' + Math.random().toString(36).substring(2)
            if (trackIndex > -1) {
              if (this.bullets[trackIndex].length) {
                this.bullets[trackIndex].push({ id })
              } else {
                this.bullets[trackIndex] = [{ id }]
              }

              this.items.push({
                id,
                text: item.blessContent,
                top: trackTop,
                trackIndex,
                duration,
              })
              // push成功了才继续插入下一个
              this.i++
            }
          })
          .exec()
      })
    },
    // 获取空闲轨道
    getTrackIndex(param) {
      return new Promise(resolve => {
        const readyIdxs = []
        let index = -1
        // 优先去 idle 状态
        this.tracks.forEach((v, idx) => v === 'idle' && readyIdxs.push(idx))
        if (readyIdxs.length) {
          // 可以插入任意一条空闲的轨道，也可以每次都直接取空闲轨道中的第一条
          // const random = parseInt(Math.random() * (readyIdxs.length))
          // index = readyIdxs[random];
          index = readyIdxs[0]
          this.tracks[index] = 'running'
          resolve(index)
        }
        // 没有轨道空闲，丛上到下巡检各轨道，选出可执行弹幕轨道
        for (let i = 0; i < this.bullets.length; i++) {
          const len = this.bullets[i].length
          if (len) {
            const item = this.bullets[i][len - 1]
            this.checkTrack(item.id, flag => {
              //直接按照从上到下来显示，不需要去计算，直接把弹幕间隔时间加长就行了
              resolve(param%this.bullets.length)
              // if (item && flag) {
              //   resolve(i)
              // } else if (i === this.bullets.length - 1) {
              //   resolve(-1)
              // }
            })
          }
        }
      })
    },
    checkTrack(id, cb) {
      const query = uni.createSelectorQuery().in(this)

      query
        .select('#' + id)
        .boundingClientRect(data => {
          const itemPos = data
          // 轨道中最后一个元素尚未完全进入展示区域，直接跳出
          if (itemPos.right > this.targetRight) {
            cb(false)
          } else if (itemPos.right < this.targetRight) {
            // 轨道内最后一条弹幕和新弹幕的追及问题
            // 轨道中最后一个元素已完全进去展示区域
            // 速度相同，只要初始条件满足即可，不用去考虑追及问题
            if (this.speed) {
              cb(true)
            } else {
              // this.compare(itemPos, index, len)
              // 原弹幕速度
              const v1 = (this.targetW + itemPos.width) / +itemPos.dataset.duration
              /**
               * 新弹幕
               * s2：全程距离
               * t2：全程时间
               * v2：速度
               */
              const s2 = this.targetW + this.bulletInfo.width
              const t2 = this.bulletInfo.duration
              const v2 = s2 / t2

              if (v2 <= v1) {
                cb(true)
              } else {
                // t = s / v  比较时间：t1, t2
                // 原弹幕跑完剩余全程所需要的时间
                const t1 = (itemPos.right - 0) / v1
                // 新弹幕头部跑完全程所需要的时间
                const t2 = this.targetW / v2
                // console.log('前面的--->', t1, t2, '后面的时间', v1)
                if (t2 < t1) {
                  cb(false)
                }
              }
            }
          }
        })
        .exec()
    },
    handleEnd(id, trackIndex) {
      // 从轨道中剔除已经结束动画的弹幕
      this.bullets[trackIndex] = this.bullets[trackIndex].filter(v => v.id !== id)

      if (!this.bullets[trackIndex].length) {
        this.tracks[trackIndex] = 'idle'
      }
    },
  },
}
</script>
<style lang="scss">
.l-barrage {
  position: relative;
  z-index: 3;
  width: 100vw; // 弹幕区域的宽度为屏幕的宽度
  height: 100rpx; // 这里的高度可以根据实际需要显示弹幕的行数来设定，如每行高度为24px,那么我们可以设置大于

  .barrage-item {
    position: absolute;
    left: -100rpx;
    padding: 0 16rpx;
    white-space: nowrap;
    color: #000000;
    font-size: 30rpx;
    background-color: rgba(#FFE0CA, 0.5);
    border-radius: 20rpx;
    animation: mymove 10s linear forwards;
  }

  .bullet-container {
    position: absolute;
    right: 9999rpx;
    visibility: hidden;
    white-space: nowrap;
  }
}

@keyframes mymove {
  from {
    transform: translateX(100vw);
  }
  to {
    transform: translateX(-100%);
  }
}

@-moz-keyframes mymove /* Firefox */ {
  from {
    transform: translateX(100vw);
  }
  to {
    transform: translateX(-100%);
  }
}

@-webkit-keyframes mymove /* Safari and Chrome */ {
  from {
    transform: translateX(100vw);
  }
  to {
    transform: translateX(-100%);
  }
}

@-o-keyframes mymove /* Opera */ {
  from {
    transform: translateX(100vw);
  }
  to {
    transform: translateX(-100%);
  }
}
</style>
